package fr.cephb.operon.localcmd.snp;


import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.HashMap;


import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;


import fr.cephb.joperon.core.Operon;
import fr.cephb.joperon.core.bio.Assembly;
import fr.cephb.joperon.core.db.SnpMapDB;
import fr.cephb.joperon.core.entities.SnpMap;
import fr.cephb.operon.common.CmdLineApplication;

public class SnpMapFilterByJavascript
	extends CmdLineApplication
	{
	/** assembly used */
	private Assembly assembly=null;
	
	
	public SnpMapFilterByJavascript()
		{
		
		}
	
	private Assembly getAssembly() {
		return assembly;
		}
	
	
	private void run(ScriptEngine engine,CompiledScript script) throws IOException
		{
		
		PrintWriter out= getWriter();
		Operon operon=null;
		SnpMapDB mapdb =null;
		SnpMapDB.ValueIterator iter=null;
		try {
			HashMap<String, Object> context=new HashMap<String, Object>();
			Bindings bindings = engine.createBindings();
			
			log().fine("Opening Operon with "+getBerkeleyDBHome());
			operon= Operon.newInstance(getBerkeleyDBHome());
			mapdb = new SnpMapDB(operon,getAssembly());
			log().fine("Opening SnpMap with "+mapdb.getName());
			mapdb.open();
			iter = mapdb.listValues();
			int rowIndex=0;
			while(iter.hasNext())
				{
				SnpMap map= iter.next();
				bindings.put("snp", map);
				bindings.put("rowIndex", rowIndex);
				bindings.put("mapContext", context);
				script.eval(bindings);
				
				++rowIndex;
				if(Boolean.TRUE.equals(context.get("stop")))
					{
					log().fine("User ask to close the loop");
					break;
					}
				}
			bindings.put("snp", null);
			bindings.put("rowIndex", rowIndex);
			bindings.put("mapContext", context);
			script.eval(bindings);
			} 
		catch (IOException e) {
			throw e;
			}
		catch (Exception e) {
			throw new IOException(e);
			}
		finally
			{
			if(iter!=null) iter.close();
			log().fine("Closing database");
			if(mapdb!=null) mapdb.safeClose();
			log().fine("Closing Operon");
			if(operon!=null) operon.safeClose();
			out.flush();
			}
		}
	
	
	@Override
	protected void usage(PrintStream out) {
		out.println("Author: Pierre Lindenbaum PhD.");
		out.println("Filter the SNPMAP database with a javascript program. .");
		super.usage(out);
		out.println(" -a <assemby-id> default:"+Assembly.getDefaultAssembly().getId());
		for(Assembly as: Assembly.getAssemblies())
			{
			out.println("   "+as.getId()+" : "+as.getName());
			}
		out.println("<javascript file>");
		
		out.println("\nVariable passed to the script:\n");
		out.println("  * 'snp' the current SnpMap. Will be null when the last row is reached");
		out.println("  * 'rowIndex' current row index");
		out.println("  * 'mapContext' an associative array passed to each call of the script. put \"stop\" to break the scan");
		
		}
	
	
	@Override
	protected int parseOption(String[] args, int optind)
		{
		int i= super.parseOption(args, optind);
		if(i!=-1) return i;
		//assembly
		if(args[optind].equals("-a"))
			{
			this.assembly= Assembly.getAssemblyById(Integer.parseInt(args[++optind]));
			}
		else
			{
			return -1;
			}
		return optind;
		}
	
	/** main entry for this program */
	public static void main(String[] args)
		{
		try {
			SnpMapFilterByJavascript app= new SnpMapFilterByJavascript();
			int optind=0;
			int index=0;
			//loop over arguments
		    while(optind<args.length)
				{
				if((index=app.parseOption(args,optind))!=-1)
				 	{
					optind= index; 
				 	}
				 else if (args[optind].equals("-e"))
				     {
				     ++optind;
				     break;
				     }
				 else if (args[optind].equals("--"))
				     {
				     ++optind;
				     break;
				     }
				else if (args[optind].startsWith("-"))
				     {
				     System.err.println("bad argument " + args[optind]);
				     System.exit(-1);
				     }
				else
				     {
				     break;
				     }
				++optind;
				}
		    //check assembly
		    if(app.assembly==null)
		    	{
		    	app.assembly= Assembly.getDefaultAssembly();
		    	app.log().info("Set assembly to "+app.assembly);
		    	}
		    
		    
		    //read from stdin
		    if(optind+1!=args.length)
		    	{
		    	System.err.println("Illegal number of argumernts");
		    	return;
		    	}
		    
		    app.log().finest("Load javascript engine");
		    // create a script engine manager
	        ScriptEngineManager factory = new ScriptEngineManager();
	        // create JavaScript engine
	        ScriptEngine engine = factory.getEngineByName("js");
	        if(engine==null)
	        	{
	        	System.err.println("Javascript library binding missing");
	        	return;
	        	}
	        Compilable compilingEngine = (Compilable)engine;
	        //compile the script
	        System.err.println("Opening "+args[optind]+ " "+new File(args[optind]).exists());
	        FileReader scriptIn= new FileReader(args[optind++]);
	        CompiledScript script=compilingEngine.compile(scriptIn);
	        scriptIn.close();
		    app.run(engine,script);
		    app.log().finest("Done.");
			}
		catch (Exception e)
			{
			e.printStackTrace();
			}
		}
	}
